【新機能】AWS ELBのApplication Load Balancer(ALB)の認証機能でWebアプリにGoogle認証を追加する
ども、大瀧です。
本日、AWS ELBのApplication Load Balancer(ALB)に認証機能が追加されました。
ALBの認証機能では様々なことができるのですが独自IdPとの連携は既に他のメンバーが記事を書いているので、本記事ではOpenID Connectの例としてGoogle Identity PlatformによるGoogleアカウント認証を設定する様子をご紹介します。
構成の概要
今回の構成図を示します。OpenID ConnectのAuthorization Code Flowに従うのですが、フローが複雑なのでALBの設定に関わるところだけざっくり解説します。
ALBへの初回(未認証)アクセスはGoogleのログイン画面にリダイレクトし、その画面でユーザーはGoogleアカウントで認証します。認証に通ると認証コードを付与して再度ALBにリダイレクトされ、その認証コードを使ってALBはGoogle APIとごにょごにょして最終的にユーザークレーム(ユーザー情報)を取得、ターゲット(EC2やECS)に転送するリクエストヘッダに付与します。クライアントにはセッションCookieを発行し、2回目以降はCookieを以て認証をスキップします。ごく一般的なフローですね。
必要なもの
- Googleアカウント
- ALBのHTTPSリスナに設定するTLS証明書(ACMでも持ち込みでも可)
ステップ1. Google Identity PlatformでのOAuth2.0 クライアントIDの発行
まずはGoogleのOpenID Providerを利用するために必要なOAuth2.0 クライアントIDを発行します。Googleアカウントでログインした状態でGoogle APIの管理コンソールにある認証情報にアクセスします。
[認証情報を作成]ボタンから「OAuthクライアントID」を選択します。
[アプリケーションの種類]では「ウェブ アプリケーション」を選択、任意の名前を設定し、承認済みのリダイレクトURIにはhttps://<ELBに設定するドメイン>/oauth2/idpresponse
を入力、[作成]ボタンをクリックします。
クライアントIDとクライアントシークレットが表示されるので、コピーしておき[OK]をクリックします。
また、Google OpenID Providerの各エンドポイントは以下のURLのレスポンスとして一覧できるので、取得しておきます。
$ curl https://accounts.google.com/.well-known/openid-configuration { "issuer": "https://accounts.google.com", "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth", "token_endpoint": "https://www.googleapis.com/oauth2/v4/token", "userinfo_endpoint": "https://www.googleapis.com/oauth2/v3/userinfo", "revocation_endpoint": "https://accounts.google.com/o/oauth2/revoke", "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs", "response_types_supported": [ "code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token", "none" ], "subject_types_supported": [ "public" ], "id_token_signing_alg_values_supported": [ "RS256" ], "scopes_supported": [ "openid", "email", "profile" ], "token_endpoint_auth_methods_supported": [ "client_secret_post", "client_secret_basic" ], "claims_supported": [ "aud", "email", "email_verified", "exp", "family_name", "given_name", "iat", "iss", "locale", "name", "picture", "sub" ], "code_challenge_methods_supported": [ "plain", "S256" ] }
これでOKです。
ステップ2. ALBの設定
ステップ1の情報を元に、ALBにOpenID Connectによる認証を設定します。今回は既存のALBにHTTPSリスナを追加し、合わせて認証を有効にします。
EC2管理画面の[ロードバランサ]からALBを選択し、[リスナー]タブの「リスナーの追加」ボタンをクリックします。
リスナーの追加画面ではプロトコルを「HTTPS」、ポートはデフォルトの443番とします。デフォルトアクションの[+ アクションの追加]から「認証...」をクリックします。
[認証]では「OIDC」を選択、各項目はステップ1で確認したエンドポイントと取得したクライアントID/シークレットを貼り付けます。
画面を下にスクロールし、チェックマークをクリックして保存します。続いて再度[+ アクションの追加]から「転送先...」をクリックし、ターゲットグループを登録します。
その他、TLSのセキュリティポリシーやTLS証明書を選択し、右上の[保存]ボタンをクリックすれば設定完了です。
動作確認
では、WebブラウザからALBにアクセスしてみると...
おなじみのGoogle認証画面にリダイレクトされました!認証情報を入力すると...
ELBからターゲットのレスポンスが表示され、正常にアクセスできました。ちなみにこのURL(/dump
)ではリクエストヘッダをレスポンスにダンプするようWebアプリケーションを動作させており、ALBが付与するユーザークレームであるX-Amzn-Oidc-*
ヘッダが確認出来ますね。
JWTの検証
ターゲットに転送されるX-Amzn-Oidc-Data
ヘッダにはALBによって署名されたJWTのトークンが入るので、これをターゲット側で検証することが出来ます。検証で使う公開鍵は、以下のURLから取得出来ます。
https://public-keys.auth.elb.<リージョン名>1.amazonaws.com/<JWTヘッダのkid>
$ curl https://public-keys.auth.elb.ap-northeast-1.amazonaws.com/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX -----BEGIN PUBLIC KEY----- MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEhnw6CUZ6i1mIvMx4CJmwObLc2QN81IML GDFqjFwet/7e9HDFFPG8I9/PSK4pVG6CZtt2m1bXAyOuwo+5xZPTGw== -----END PUBLIC KEY-----
では、jwt.ioで検証してみます。Encodedにトークンをペーストするとデコードされたヘッダおよびペイロード(ユーザークレーム)が確認できます。
さらに[VERIFY SIGNATURE]の1つ目のテキストエリアに公開鍵をペーストすると「Signature Verified」と表示され、検証できました!
ELBのログ
ALBのアクセスログはS3に格納されます。今回の認証に係るログは以下のようになりました。
h2 2018-05-31T04:59:51.812260Z app/<ALB名>/XXXXXXXXXXXXXXXX <リモートIP>:57497 - -1 -1 -1 302 - 206 543 "GET https://<ドメイン名>:443/dump HTTP/2.0" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:60.0) Gecko/20100101 Firefox/60.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 - "Root=1-XXXXXXXX-XXXXXXXXXXXXXXXX" "<ドメイン名>" "arn:aws:acm:ap-northeast-1:<アカウントID>:certificate/bef10a17-abf3-4aa1-a8fb-76416f9de10f" 0 2018-05-31T04:59:51.811000Z "authenticate" h2 2018-05-31T05:03:42.955191Z app/<ALB名>/XXXXXXXXXXXXXXXX <リモートIP>:57745 - -1 -1 -1 302 - 552 986 "GET https://<ドメイン名>:443/oauth2/idpresponse?state=XXXXXXXXXXXXXXXX&code=XXXXXXXXXXXXXXXX&authuser=0&session_state=XXXXXXXXXXXXXXXX&prompt=none HTTP/2.0" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:60.0) Gecko/20100101 Firefox/60.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 - "Root=1-5b0f822e-5824a8c6c44fa81b0da2cb48" "<ドメイン名>" "session-reused" -1 2018-05-31T05:03:42.459000Z "authenticate" h2 2018-05-31T05:03:42.992511Z app/<ALB名>/XXXXXXXXXXXXXXXX <リモートIP>:57745 <ターゲットのIP>:32771 0.001 0.003 0.000 200 200 718 1691 "GET https://<ドメイン名>:443/dump HTTP/2.0" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:60.0) Gecko/20100101 Firefox/60.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:ap-northeast-1:<アカウントID>:targetgroup/tg0/XXXXXXXXXXXXXXXX "Root=1-XXXXXXXX-XXXXXXXXXXXXXXXX" "<ドメイン名>" "session-reused" 0 2018-05-31T05:03:42.988000Z "authenticate,forward"
1,2行目が初回の認証周り、3行目はターゲットへの転送にかかるログですね。
まとめ
Application Load Balancer(ALB)の認証機能のひとつ、OpenID Connect連携の例としてWebアプリにGoogle認証をかける様子をご紹介しました。既存のWebアプリケーションに手軽にアクセス制限をかけられる、ナイス機能だと思います。ALBによるWebアプリケーションの構築に役立ててください。
続編があります
この構成は任意のGoogleアカウントの認証を通してしまうので、Google Appsの自組織のメールアドレスのみ許可したいというような業務向けのユースケースだとターゲット側で認可機能を実装しなくてはならず、片手落ち感があります。少し複雑になりますが、Amazon Cognitoユーザープールでドメイン縛りを追加した記事を書きました。
こちらもどうぞ!